// apply/call/bind
// curry/compose
// debounce/throttle
// get/memoized/chunk/countBy
// settimout 实现 setinterval

Function.prototype.myApply = function (context, ...args) {
    const key = Symbol()
    context[key] = this;
    let res = context[key](...args)
    delete context[key]
    return res;
}

Function.prototype.myCall = function (context, args) {
    const key = Symbol()
    context[key] = this;
    let res;
    if (Array.isArray(args)) {
        res = context[key](...args)
    } else {
        res = context[key]()
    }
    delete context[key]
    return res;
}

Function.prototype.myBind = function (context) {
    return (...args) => {
        const key = Symbol()
        context[key] = this;
        let res = context[key](...args)
        delete context[key]
        return res;
    }
}


// 柯里化
function curry(fn) {
    function funCurry(...args) {
        if (args.length === fn.length) {
            return fn(...args)
        } else {
            return (...params) => {
                return funCurry(...[...args, ...params])
            }
        }
    }
    return funCurry
}
const a = (a, b, c, d) => a + b + c + d
const fn = curry(a)
console.log(fn(1, 2)(2, 4))

// 参数无限的柯里化
function currySum(...args) {
    let arr = args
    function funCurry(...params) {
        arr = [...arr, ...params]
        return funCurry
    }

    funCurry.toString = () => {
        return arr.reduce((pre, next) => pre + next, 0)
    }
    return funCurry
}
console.log(currySum(1, 2, 3, 4)(5)(6, 7).toString())


// compose 函数
function compose(...fns) {
    return (params) => {
        return fns.reduceRight((pre, cur) => cur(pre), params)
    }
}
var fn1 = (a) => a + a
var fn2 = (a) => a * a
console.log(fn2(fn1(4)))
console.log(compose(fn2, fn1)(4))

function debounce(fn, delay) {
    let timer = null
    return (...args) => {
        if (timer) clearTimeout(tiemr)
        timer = setTimeout(() => {
            fn.apply(this, args)
            timer = null
        }, delay);
    }
}

// 立即执行是第一次执行，后面
function debounce2(fn, delay, immediate) {
    let timer = null
    return (...args) => {
        if (timer) clearTimeout(timer)
        if (immediate) {
            if (!timer) fn.apply(this, args)
            timer = setTimeout(() => {
                timer = null
            }, delay)
        } else {
            timer = setTimeout(() => {
                fn.apply(this, args)
                timer = null
            }, delay);
        }
    }
}

function throttle(fn, delay) {
    let timer = null
    return (...args) => {
        if (timer) return;
        timer = setTimeout(() => {
            fn.apply(this, args)
            timer = null
        }, delay)
    }
}

// 更准确的
function throttle2(fn, delay) {
    let timer = null
    let start = Date.now()
    return (...args) => {
        let end = Date.now()
        let wait = delay - (end - start)
        if (wait <= 0) {
            if(timer){
                clearTimeout(timer)
                timer = null
            }
            fn.apply(this, args)
            start = Date.now()
        } else {
            if(timer) return
            timer = setTimeout(() => {
                fn.apply(this, args)
                timer = null
                start = Date.now()
            }, wait)
        }
    }
}

function myGet(obj, path, defaultValue){
    if(!Array.isArray(path)){
        path = path.match(/[^\[\].]+/g) || []
    }

    let res = obj
    for(let i = 0; i< path.length; i++){
        if(!res) return defaultValue
        res = res[path[i]]
    }
    return res || defaultValue
}
const obj = {a:[{b: {c: 3}}]}
console.log(myGet(obj, ['a','0','b', 'c']))
console.log(myGet(obj, "a[0].b.c", 3))

// reslover 计算的方法不变，就用缓存
function memoized(fn,reslover){
    var memorizeFn = function(...args){
        const key = reslover ? reslover.apply(this, args) : args[0]
        if(memorizeFn.cache.has(key)){
            return memorizeFn.cache.get(key)
        }
        const result = fn.apply(this, args)
        memorizeFn.cache.set(key, result)
        return result
    }
    memorizeFn.cache = new WeakMap()
    return memorizeFn
}

var object = {a: 1, b: 2}
var other = {c: 3, d: 4}

var values = memoized((object) => Object.values(object))
// 每次返回新对象，都要重新算没有缓存
var values2 = memoized((object) => Object.values(object), () => ({}))

console.log(values2(object))
object.a = 2;
console.log(values2(object))




function chunk(arr, size){
    if(size < 1) return [];
    const result = [];
    for(let i = 0; i< arr.length; i+= size){
        result.push(arr.slice(i, i + size));
    }
    return result
}
console.log(chunk(['a', 'b', 'c','d']))




function countBy(collection, iteratee) {
    const result = {};
    for(const item of collection){
        const key = iteratee(item)
        result[key] = result[key] ? result[key] + 1 : 1
    }
    return result;
}
const user = [
    {user: 1, active: true},
    {user: 2, active: true},
    {user: 3, active: false}
]
console.log(countBy(user, (value) =>  value.active))



function mySetInterval(fn, time) {
    function warpTime(...args) {
        setTimeout(warpTime, time); // 注意// 注意
        fn.call(this, ...args)
    }
    setTimeout(warpTime, time);
}